package com.test.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class Client {

    ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
    ByteBuffer readBuffer = ByteBuffer.allocate(1024);

    public void start() throws IOException {
        // 打开socket通道
        SocketChannel sc = SocketChannel.open();
        // 设置为非阻塞
        sc.configureBlocking(false);
        // 连接服务器地址和端口
        sc.connect(new InetSocketAddress("localhost", 8001));
        // 创建选择器
        Selector selector = Selector.open();
        // 注册连接服务器socket的CONNECT事件
        sc.register(selector, SelectionKey.OP_CONNECT);

        Scanner scanner = new Scanner(System.in);
        // while (true)  保持连接，持续处理事件
        while (true) {
            // 该调用会阻塞，直至至少有一个事件发生
            // 比如上面 connect() 连接服务端成功就会触发 CONNECT 事件
            selector.select();
            // 获取发生事件的SelectionKey
            Set<SelectionKey> keys = selector.selectedKeys();
            // System.out.println("keys=" + keys.size());
            Iterator<SelectionKey> keyIterator = keys.iterator();
            while (keyIterator.hasNext()) {
                 SelectionKey key = keyIterator.next();
//                keyIterator.remove();
                // 连接事件
                if (key.isConnectable()) {
                    System.out.println("OP_CONNECT");
                    sc.finishConnect();

                    System.out.println("Server connected...");

                    // 注册WRITE事件，准备读取用户输入
                    sc.register(selector, SelectionKey.OP_WRITE);

                    // 写事件
                } else if (key.isWritable()) {
                    // System.out.println("OP_WRITE");
                    System.out.print("please input message:");
                    String message = scanner.nextLine();
                    // 向缓冲区写数据前先清空
                    writeBuffer.clear();
                    writeBuffer.put(message.getBytes());
                    // 这里必须 filp()，因为 put() 后 position 等于消息的length，所以server拿到后无法再从这个缓冲区读出数据
                    writeBuffer.flip();

                    sc.write(writeBuffer);


                    // 注册读操作，下一次读取
                    sc.register(selector, SelectionKey.OP_READ);

                    // 读事件
                } else if (key.isReadable()){
                    // System.out.println("OP_READ");
                    System.out.print("receive message: ");

                    // 清空缓冲区
                    readBuffer.clear();
                    // 将通道中的数据读到缓冲区 readBuffer
                    // numRead 记录读了多少字符
                    int numRead = sc.read(this.readBuffer);
                    // 通过 buffer.array() 拿到buffer中的数组（没必要buffer.filp）
                    System.out.println(new String(readBuffer.array(),0, numRead));

                    // 将当前SocketChannel再注册上WRITE事件
                    sc.register(selector, SelectionKey.OP_WRITE);
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new Client().start();
    }

}

